<?php

namespace app\devices\controller\api;

use app\data\service\PaymentService;
use app\data\service\UserService;
use app\devices\service\BoxService;
use app\devices\service\DevicesService;
use app\devices\service\OrderService;
use app\devices\service\SocketService;
use app\devices\service\VipService;
use think\admin\extend\CodeExtend;
use think\cache\driver\Redis;

/**
 * 订单接口控制器
 * Class Order
 * @package app\devices\controller\api
 */
class Order extends Auth
{
    /**
     * 控制器初始化
     */
    protected function initialize()
    {
        parent::initialize();
        if (empty($this->user['status'])) {
            $this->error('账户已被冻结，不能操作订单数据哦！');
        }
    }

    /**
     * 获取订单列表
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function get()
    {
        $data = $this->_vali([
            'status.require'=>'订单状态不能为空',
            'status.between:1,2'=>'订单状态输入有误',
        ]);

        $map = ['user_id' => $this->uuid, 'deleted' => 0, 'pid'=>0];

        $field = "id, order_no, create_time, reality_end_time, money, amount, amount_total, extends, s_id, status, fee_type, free_hours, create_at, store_id, out_times";

        $query = $this->_query('JcgDevicesOrder')->equal('status,order_no');
        $result = $query->field($field)->where($map)->order('id desc')
            ->page(true, false, false, 10);
        if (count($result['list']) > 0) OrderService::instance()->buildItemData($result['list'], $data['status'], $this->user['phone']??'');
        $this->success('获取订单数据成功！', $result);
    }

    public function buyVip(){
        $redis = new Redis();
        $caches =  $redis->get("addOrder".$this->uuid);

        if($caches){
            $this->error("操作过快");
        }

        $data = $this->_vali(
            [
                'vip_id.require'=>'购买VIP id 不可为空',
                'payment_back.default' => '', # 支付回跳地址
                'code.default'=>'M7189778927525',
            ]
        );

        $vip = VipService::instance()->getVipInfo($data['vip_id']);
        if(empty($vip)){
            $this->error("当前购买的会员类型不存在");
        }

        unset($vip['id'], $vip['summary'], $vip['sort'], $vip['create_at'], $vip['update_at']);

        // 订单详情处理
        $order = [
            'user_id' => $this->uuid,
            'order_no' => CodeExtend::uniqidDate(18, 'V'),
            'money' => $vip['amount'],
            'status' => 0,
            'extends' =>json_encode($vip, JSON_UNESCAPED_UNICODE)
        ];

        $this->app->db->startTrans();
        $payInfo = [];
        $order_id = 0;
        try {
            // 写入订单数据
            $order_id = $this->app->db->name('JcgVipOrder')->insertGetId($order);

            if($order['money']<0.01){

            }else{
                $user = $this->user;
                $openid = '';
                if (in_array($this->type, [UserService::APITYPE_WXAPP, UserService::APITYPE_WECHAT])) {
                    if($data['code']){
                        $openid = $user['openid2'];
                    }else{
                        $openid = $user[UserService::TYPES[$this->type]['auth']] ?? '';
                    }
                    if (empty($openid)) {
                        $this->app->db->rollback();
                        $this->error("无法创建支付");
                    }
                }

                if($order['money']>0){
                    if(sysconf('wechat.appid')=='wx0d7d455b9edc9ada'){
                        $order['money'] = 0.01;
                    }
                    if($user['appid']){
                        $openids = explode("+_+",$openid);
                        $openid = current($openids);
                        $pay_codes = ['wx484d58740cc35f49'=>'M7238339972234'];
                        $data['code'] = $pay_codes[end($openids)]??'';
                        if(empty($data['code'])){
                            $this->error("无法创建支付");
                        }
                    }
                    $payInfo = PaymentService::instance($data['code'])->create($openid, $order['order_no'], $order['money'], '寄存柜购买会员', '', $data['payment_back'], 'vip_order');
                }
            }

            $this->app->db->commit();
        } catch (\Exception $exception) {
            $this->app->db->rollback();
            $this->error("购买会员下单失败，{$exception->getMessage()}");
        }

        // 返回处理成功数据
        $this->success('购买会员下单成功', ['payInfo'=>$payInfo, 'order_id'=>$order_id]);
    }

    /**
     * 用户创建订单
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function add()
    {
        $redis = new Redis();
        $caches =  $redis->get("addOrder".$this->uuid);

        if($caches){
            $this->error("操作过快");
        }

        $data = $this->_vali(
            [
                'd_id.require' => '设备ID不能为空',
                'box_id.require' => '箱门ID不能为空',
                'pay_type.default'=>'2',
                'payment_back.default' => '', # 支付回跳地址
                'code.default'=>'M7134848170858',
                'pick_up_code.default'=>''
            ]
        );

        if($data['pick_up_code'] && strlen($data['pick_up_code'])!=4){
            $this->error("请输入四位取件码");
        }

        $useBoxUser = $redis->get("useBox".$data['box_id']);
        if($useBoxUser && $useBoxUser!=$this->uuid){
            $this->error("箱格已被他人使用");
        }
        $redis->delete("useBox".$redis->get("UserUseBox".$this->uuid));
        $redis->set("useBox".$data['box_id'], $this->uuid, 120);
        $redis->set("UserUseBox".$this->uuid, $data['box_id'], 120);

        $device = DevicesService::instance()->getDevice($data['d_id']);
        if(empty($device)){
            $this->error("设备不存在");
        }

        $store = DevicesService::instance()->getStore($device['store_id']);
        if($store['bus_hours']<1){
            $end = explode(":", $store['end_hours']);
            $start = explode(":", $store['start_hours']);

            if(date("Hi")<(int)($start[0].$start[1]) || date("Hi")>(int)($end[0].$end[1])){
                $this->error('当前不在营业时间');
            }
        }

        $boxService = new BoxService($this->app);
        $box = $boxService->getBox($data['box_id']);

        if($box['d_id']!=$data['d_id']){
            $this->error("箱格异常");
        }

        if(!($box['box_id']??0)){
            $this->error("箱格异常");
        }else if($box['user_id'] && $box['user_id']!=$this->uuid){
            $this->error("箱格已被使用");
        }
        else if($box['box_status']!=1 && $box['user_id']!=$this->uuid){
            $this->error("箱格处于".$boxService->box_status[$box['box_status']].'状态');
        }
        else if($box['is_lock']<1){
            $this->error("箱门已打开，请先关闭箱门");
        }
        else if($box['is_lock']==2){
            $this->error("箱格锁异常");
        }

        $box_order = OrderService::instance()->get('', ['box_id'=>$box['box_id'], 'status'=>1]);
        if($box_order['id']){
            $redis->handler()->ZADD("orderUseBox".$box_order['d_id'], $box_order['user_id'], $box_order['box_id']);
            //更新箱子状态为使用中
            BoxService::instance()->updateBox($box_order['box_id'], ['box_status'=>2, 'user_id'=>$box_order['user_id']]);
            $this->error("箱格已被使用了");
        }

        $boxs[] = $box??[];

        $boxService->buildData($boxs, $device['fee_id']);

        $rank = $redis->handler()->ZRANK("UserDiscounts", $this->uuid);
        if(is_numeric($rank)){
            $discounts = $redis->handler()->Zscore("UserDiscounts", $this->uuid);
            if($discounts>0){
                $boxs[0]['discounts'] = $discounts;
                $boxs[0]['m_fee_price'] = $boxs[0]['fee_price'];
                $boxs[0]['fee_price'] = $boxs[0]['fee_price']?sprintf("%.2f", $boxs[0]['fee_price']*($discounts/100)):0;
                $boxs[0]['price'] = $boxs[0]['price']?sprintf("%.2f", $boxs[0]['price']*($discounts/100)):0;
            }else{
                $boxs[0]['fee_price'] = $boxs[0]['price'] = 0;
            }
        }
        $vip = VipService::instance()->getUserVip($this->uuid);
        if($vip){
            $boxs[0]['fee_price'] = $boxs[0]['price'] = 0;
        }

        // 订单数据
        $item['order_no'] = CodeExtend::uniqidDate(18, 'N');
        $item['amount'] = $boxs[0]['fee_price'];
        $item['pay_type'] = $data['pay_type'];

        $half_type = $device['half_type'];

        $extends = ['box_type'=>$boxs[0]['box_type'], 'box_num'=>$box['box_num'], 'store_name'=>$device['device_name'], 'device_name'=>$device['device_name'],'contact_phone'=>$device['contact_number'], 'max_money'=>$boxs[0]['max_money']??0, "half_type"=>$half_type, "half_hour"=>$device['half_hour'],"store_contact"=>$store['contact_number']];

        // 订单详情处理
        $order = [
            'user_id' => $this->uuid,
            'order_no' => $item['order_no'],
            'store_id' => $device['store_id'],
            'd_id' => $data['d_id'],
            'device_board' => $box['device_board'],
            'board_channel' => $box['board_channel'],
            'box_id' => $data['box_id'],
            'amount' => $boxs[0]['price']??0,
            'money' => $item['amount']??0,
            'pay_type' =>$item['pay_type'],
            'fee_type' => $boxs[0]['fee_type'],
            'status' =>$boxs[0]['fee_type']==1 || ($item['amount']??0)==0?1:0,
            'share_ratio'=>$store['share_ratio'],
            'free_hours'=>$boxs[0]['free_hours']??0,
            'is_check'=>0,
            'create_time'=>($item['amount']??0)?0:time(),
            'extends' =>json_encode($extends, JSON_UNESCAPED_UNICODE)
        ];

        $this->app->db->startTrans();
        $payInfo = [];
        $order_id = 0;


        $user = $this->user;
        try {
            // 写入订单数据
            $order_id = $this->app->db->name('JcgDevicesOrder')->insertGetId($order);

            $box_ids = $this->app->db->name("JcgDevicesOrder")->where("id", "<>", $order_id)->where(['status'=>0, 'user_id'=>$this->uuid, 'is_check'=>0])->column("box_id");
            if($box_ids){
                $this->app->db->name("JcgBoxs")->where("id", "in", $box_ids)->update(['box_status'=>1, 'user_id'=>0]);
            }

            //更新箱子状态为使用中
            BoxService::instance()->updateBox($box['box_id'], ['box_status'=>2, 'user_id'=>$this->uuid]);

            if($boxs[0]['fee_type']==1 || $order['money']<0.01){
                $payInfo = [];
                $msg = [
                    'type'=>2,
                    'device_id'=>$device['device_id'],
                    'ch'=>$order['board_channel'],
                    'device_board'=>$order['device_board']
                ];
                SocketService::instance()->sendSocket($msg);
                $redis->handler()->ZADD("orderUseBox".$order['d_id'], $order['user_id'], $order['box_id']);

                if($data['pick_up_code']){
                    $pick_data = ['user_id'=>$this->uuid, 'mobile'=>$this->user['phone'], 'd_id'=>$data['d_id'], 'device_no'=>$device['device_no'],'box_num'=>$box['box_num'], 'box_type'=>$boxs[0]['box_type'],'order_id'=>$order_id, 'box_id'=>$box['box_id'], 'code'=>$data['pick_up_code']];
                    $res = DevicesService::instance()->setPickUpCode($pick_data);
                    if(!$res){
                        $this->app->db->rollback();
                        $this->error("创建订单失败");
                    }
                }

                $openid = $this->user['openid2'];
                if($openid){
                    $extends = json_decode($order['extends'], true);
                    OrderService::instance()->sendTemp(1, $openid, [$extends['device_name'], ($extends['box_num']<10?'0'.$extends['box_num']:$extends['box_num']).'号', (empty($order['money'])?0:$order['money']).'元', date("Y-m-d H:i")], '', '请在营业时间内取包，如有疑问，请联系：'.($extends['store_contact']??''));
                }

                //添加redis 开锁命令
                $redis->handler()->ZADD("UnLockCommand", $order['user_id'], $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);

                $redis->set("PayBox_".$order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel'], $order_id,86400*3);

                jcgoplog('开锁','用户免费开锁', $user['nickname'], $user['id'], $order_id, $order['box_id']);
            }else{
                $openid = '';
                if (in_array($this->type, [UserService::APITYPE_WXAPP, UserService::APITYPE_WECHAT])) {
                    if($data['code']){
                        $openid = $user['openid2'];
                    }else{
                        $openid = $user[UserService::TYPES[$this->type]['auth']] ?? '';
                    }
                    if (empty($openid)) {
                        $this->app->db->rollback();
                        $this->error("无法创建支付");
                    }
                }

                if($data['pick_up_code']){
                    $pick_data = ['user_id'=>$this->uuid, 'mobile'=>$this->user['phone'], 'd_id'=>$data['d_id'], 'device_no'=>$device['device_no'],'box_num'=>$box['box_num'], 'box_type'=>$boxs[0]['box_type'],'order_id'=>$order_id, 'box_id'=>$box['box_id'], 'code'=>$data['pick_up_code']];
                    $redis->set("PickUpCode:".$this->uuid, $pick_data, 300);
                }
                if($order['money']>0){
                    if(sysconf('wechat.appid')=='wx0d7d455b9edc9ada'){
                        $order['money'] = 0.01;
                    }
                    if($user['appid']){
                        $openids = explode("+_+",$openid);
                        $openid = current($openids);
                        $pay_codes = ['wx484d58740cc35f49'=>'M7238339972234'];
                        $data['code'] = $pay_codes[end($openids)]??'';
                        if(empty($data['code'])){
                            $this->error("无法创建支付");
                        }
                    }
                    $payInfo = PaymentService::instance($data['code'])->create($openid, $order['order_no'], $order['money'], '寄存柜存储', '', $data['payment_back'], 'device_order');
                }
            }

            $this->app->db->commit();
        } catch (HttpResponseException $exception) {
            $this->app->db->rollback();
            throw $exception;
        } catch (\Exception $exception) {
            $this->app->db->rollback();
            $this->error("箱格下单失败，{$exception->getMessage()}");
        }

        $redis->set("order".$order_id, $order, 300);

        $redis->set("addOrder".$this->uuid, 1,5);

        // 返回处理成功数据
        $this->success('箱门下单成功', ['payInfo'=>$payInfo, 'order_id'=>$order_id, 'pick_code'=>$data['pick_up_code']]);
    }

    /**
     * 创建超时付款订单
     */
    public function outTimeAdd(){
        $redis = new Redis();
        $caches =  $redis->get("OutTimeAdd".$this->uuid);

        if($caches){
            $this->error("操作过快");
        }

        $data = $this->_vali(
            [
                'order_id.require' => '订单ID不能为空',
                'pay_type.default'=>'2',
                'code'=>'M7189778927525',
                'payment_back.default' => '', # 支付回跳地址
            ]
        );

        $money = 0;
        $order = OrderService::instance()->get('', ['id'=>$data['order_id']]);
        if($order['fee_type']==3){
            $extends = json_decode($order['extends'], true);
            $re_amount = (ceil((time()-$order['create_time'])/3600)-$order['free_hours'])*$order['amount'];
            $re_amount = $re_amount>0?$re_amount:0;
            $max_money = $extends['max_money']??0;
            if($order['money']<$re_amount){
                $money = $re_amount<$max_money?$re_amount-$order['money']:$max_money-$order['money'];
            }else{
                $this->error("订单尚未超时");
            }
        }else{
            $this->error("该订单无需付款");
        }

        // 订单数据
        $order_no = CodeExtend::uniqidDate(18, 'N');

        $order = [
            'user_id' => $this->uuid,
            'order_no' => $order_no,
            'pid'=>$data['order_id'],
            'money' => $money,
            'pay_type' =>$data['pay_type'],
            'status' =>0,
            'extends' =>json_encode(['remark'=>'超时订单'], JSON_UNESCAPED_UNICODE)
        ];

        $this->app->db->startTrans();
        $payInfo = [];
        try {
            // 写入订单数据
            $this->app->db->name('JcgDevicesOrder')->insertGetId($order);

            $openid = $this->user['openid2'];
            if (empty($openid)) {
                $this->app->db->rollback();
                $this->error("无法创建支付");
            }

            //判断测试服
            $appid = sysconf('wechat.appid')??'';
            if($appid=='wx0d7d455b9edc9ada'){
                $money = 0.01;
            }

            $payInfo = PaymentService::instance($data['code'])->create($openid, $order['order_no'], $money, '寄存柜存储', '', $data['payment_back'], 'out_order');
            $this->app->db->commit();
        } catch (HttpResponseException $exception) {
            $this->app->db->rollback();
            throw $exception;
        } catch (\Exception $exception) {
            $this->app->db->rollback();
            $this->error("箱格下单失败，{$exception->getMessage()}");
        }

        $redis->set("OutTimeAdd".$this->uuid, 1,5);

        // 返回处理成功数据
        $this->success('超时订单下单成功', ['payInfo'=>$payInfo]);
    }

    /**
     * 创建超时付款订单
     */
    public function recharge(){
        $redis = new Redis();
        $caches =  $redis->get("Recharge".$this->uuid);

        if($caches){
            $this->error("操作过快");
        }

        $data = $this->_vali(
            [
                'money.require' => '充值金额不能为空',
                'pay_type.default'=>'2',
                'code'=>'M7189778927525',
                'payment_back.default' => '', # 支付回跳地址
            ]
        );

        $money = $data['money'];

        // 订单数据
        $order_no = CodeExtend::uniqidDate(18, 'B');

        $order = [
            'uid'   => $this->uuid,
            'code'  => $order_no,
            'name'  => '用户充值',
            'amount' => $money,
            'deleted' =>1
        ];

        $this->app->db->startTrans();
        $payInfo = [];
        try {
            // 写入订单数据
            $this->app->db->name('data_user_balance')->insertGetId($order);

            $openid = $this->user['openid2'];
            if (empty($openid)) {
                $this->app->db->rollback();
                $this->error("无法创建支付");
            }

            $money = 0.01;
            $payInfo = PaymentService::instance($data['code'])->create($openid, $order['order_no'], $money, '寄存柜充值', '', $data['payment_back'], 'recharge');
            $this->app->db->commit();
        } catch (HttpResponseException $exception) {
            $this->app->db->rollback();
            throw $exception;
        } catch (\Exception $exception) {
            $this->app->db->rollback();
            $this->error("箱格下单失败，{$exception->getMessage()}");
        }

        $redis->set("OutTimeAdd".$this->uuid, 1,5);

        // 返回处理成功数据
        $this->success('超时订单下单成功', ['payInfo'=>$payInfo]);
    }

    /**
     * 订单支付成功查询接口
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function loading(){
        $data = $this->_vali([
            'order_id.require' => '订单ID不能为空',
        ]);
        $redis = new Redis();
        $order = $redis->get("order".$data['order_id']);

        if(empty($order)){
            $order = OrderService::instance()->get('', ['id'=>$data['order_id']]);
            $redis->set("order".$data['order_id'], $order, 300);
        }
        if(($order['create_time']??0)<1){
            $redis->delete("order".$data['order_id']);
        }
        if(empty($order) || $order['user_id']!=$this->uuid){
            $this->error("订单不存在");
        }

        $cmd = $redis->handler()->ZRANK("UnLockCommand", $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);

        $open_cmd = $redis->handler()->ZRANK("OPENBOX", $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);
        if(is_numeric($cmd) && !is_numeric($open_cmd)){
            $num = $redis->get("LoadingOrder".$data['order_id'])??1;
            $redis->set("LoadingOrder".$data['order_id'], $num, 60);
            $this->error("失败");
        }else{

            $redis->set("Failure", $cmd, 60);

            $extend = json_decode($order['extends'], true);

            $box_num = $extend['box_num']<10?'0'.$extend['box_num']:$extend['box_num'];

            $use_time = $order['create_time']??0;

            if($use_time<1){
                $use_time = time();
            }

            $end_time = isset($order['reality_end_time']) && $order['reality_end_time'] ? $order['reality_end_time'] : time();

            $min = ceil(($end_time - $use_time)/60);

            $use_time = date("Y-m-d H:i", $use_time);

            $use_hour = sprintf('%.1f',$min/60);

            $use_hour = $use_hour<0.1?0.1:$use_hour;

            $codes = DevicesService::instance()->getPickUpCodes($data['order_id']);

            if($order['fee_type']==3){
                $re_amount = (ceil((time()-$order['create_time'])/3600)-$order['free_hours'])*$order['amount'];
                $re_amount = $re_amount>0?$re_amount:0;
                $max_money = $extend['max_money']??0;
                if($order['money']<$re_amount){
                    $order['use_money'] = $order['money'];
                    $order['out_money'] = $re_amount<$max_money?$re_amount-$order['money']:$max_money-$order['money'];
                    $order['balance'] = 0;
                }else{
                    $order['out_money'] = 0;
                    $order['balance'] = $order['money']-$re_amount;
                    $order['use_money'] = $re_amount;
                }
                $order['fee_take_detail'] = $fee_summarys[$order['store_id']]??'';

                $order['out_money'] = sprintf('%.2f', $order['out_money']);
                $order['balance'] = sprintf('%.2f', $order['balance']);
            }

            $this->success("成功", ['order_no'=>$order['order_no'], 'box_num'=>$box_num, 'box_type'=>$extend['box_type'], 'use_time'=>$use_time, 'use_hour'=>$use_hour, 'store_name'=>$extend['store_name'], 'fee_type'=>$order['fee_type'], 'money'=>empty($order['money'])?0:$order['money'], 'mobile'=>$codes[$data['order_id']]['mobile']??0, 'pick_up_code'=>$codes[$data['order_id']]['code']??0, 'use_money'=>$order['use_money']??0]);
        }
    }

    public function payments(){
        $data = $this->_vali([
            'order_id.require' => '订单ID不能为空',
        ]);

        $redis = new Redis();
        $order = $redis->get("order".$data['order_id']);
        if(empty($order)){
            $order = OrderService::instance()->get('', ['id'=>$data['order_id']]);
        }
        if(empty($order) || $order['user_id']!=$this->uuid){
            $this->error("订单不存在");
        }

        $extend = json_decode($order['extends'], true);

        $box_num = $extend['box_num']<10?'0'.$extend['box_num']:$extend['box_num'];

        $use_time = $order['create_time']??0;

        if($use_time<1){
            $use_time = time();
        }

        $end_time = isset($order['reality_end_time']) && $order['reality_end_time'] ? $order['reality_end_time'] : time();

        $min = ceil(($end_time - $use_time)/60);

        $use_time = date("Y-m-d H:i", $use_time);

        $use_hour = sprintf('%.1f',$min/60);

        $use_hour = $use_hour<0.1?0.1:$use_hour;

        $codes = DevicesService::instance()->getPickUpCodes($data['order_id']);

        if($order['fee_type']==3){
            $re_amount = (ceil((time()-$order['create_time'])/3600)-$order['free_hours'])*$order['amount'];
            $re_amount = $re_amount>0?$re_amount:0;
            $max_money = $extend['max_money']??0;
            if($order['money']<$re_amount){
                $order['use_money'] = $order['money'];
                $order['out_money'] = $re_amount<$max_money?$re_amount-$order['money']:$max_money-$order['money'];
                $order['balance'] = 0;
            }else{
                $order['out_money'] = 0;
                $order['balance'] = $order['money']-$re_amount;
                $order['use_money'] = $re_amount;
            }
            $order['fee_take_detail'] = $fee_summarys[$order['store_id']]??'';

            $order['out_money'] = sprintf('%.2f', $order['out_money']);
            $order['balance'] = sprintf('%.2f', $order['balance']);
        }

        $this->success("成功", ['order_no'=>$order['order_no'], 'box_num'=>$box_num, 'box_type'=>$extend['box_type'], 'use_time'=>$use_time, 'use_hour'=>$use_hour, 'store_name'=>$extend['store_name'], 'fee_type'=>$order['fee_type'], 'money'=>empty($order['money'])?0:$order['money'], 'mobile'=>$codes[$data['order_id']]['mobile']??0, 'pick_up_code'=>$codes[$data['order_id']]['code']??0, 'use_money'=>$order['use_money']??0]);
    }

    /**
     * 开锁（？结束订单）
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function unlock(){
        $data = $this->_vali([
            'order_id.require'     => '订单ID不能为空',
            'temp_unlock.default'  => 0,
        ]);

        $temp_unlock = $data['temp_unlock']??0;

        $map = ['id' => $data['order_id'], 'user_id'=>$this->uuid];
        $order = OrderService::instance()->get('', $map);
        if (empty($order)) $this->error('读取订单失败');

        $extends = json_decode($order['extends'], true);

        $device = DevicesService::instance()->getDevice($order['d_id']);
        if(empty($device)){
            $this->error("设备不存在");
        }

        switch ($order['status']){
            case 0:
                $this->error('订单尚未支付');
                break;
            case 2:
                $this->error('订单已完成');
                break;
            case 3:
                $this->error('订单已完成并退余额');
                break;
        }

        $box = BoxService::instance()->getBox($order['box_id']);
        if($box['is_lock']!=1 || $box['box_status']==3){
            $device = DevicesService::instance()->getDevice($order['d_id']);
            $msg = ['type'=>1, 'device_id'=>$device['device_id'], 'device_board'=>$order['device_board']];
            SocketService::instance()->sendSocket($msg);
            usleep(600000);

            $box = BoxService::instance()->getBox($order['box_id']);
        }

        if(!($box['box_num']??0)){
            $this->error("箱格异常");
        }if($box['box_status']>2){
            $this->error("箱格处于".BoxService::instance()->box_status[$box['box_status']].'状态');
        }else if($box['user_id'] && $box['user_id']!=$this->uuid){
            $this->error("箱格已被他人使用");
        }else if($box['is_lock']<1){
            $this->error("箱门已打开，请先关闭箱门");
        }else if($box['is_lock']==2){
            $this->error("箱格锁异常");
        }

        $boxs[] = $box??[];

        BoxService::instance()->buildData($boxs, $device['fee_id']);

        $redis = new Redis();

        $out_money = 0;
        if($order['fee_type']==3){
            $re_amount = ceil((time()-$order['create_time']-$order['free_hours']*3600)/3600)*$order['amount'];
            $re_amount = $re_amount>0?$re_amount:0;
            if(in_array($order['store_id'], OrderService::instance()->special_store) && $re_amount){
                $re_amount = $re_amount<2?2:$re_amount;
            }

            $out_money = OrderService::instance()->getRefund($order);
            $max_money = $extends['max_money']??0;
            if($re_amount>$max_money){
                $re_amount = $max_money;
            }
            if($order['money']<$re_amount && !$out_money){
                $this->error("订单已超时，请付款后再开锁");
            }else{
                $order['refund'] = $order['money']-$re_amount;
            }
        }
        $half_hour = 0;
        if(($extends['half_type']??0)==1 && $order['fee_type']==3){
            $half_hour = $extends['half_hour']??0;
        }else if(($extends['half_type']??0)==1 && $order['fee_type']==2){
            $half_hour = $extends['half_hour']??0;
            $half_hour = $half_hour<0.1?1:$half_hour;
        }

        if($data['temp_unlock'] && ($extends['half_type']??0)!=1){
            $this->error('当前订单不可临时开门');
        }else if($data['temp_unlock'] && $order['fee_type']==2 && $half_hour>0 && $order['create_time']+ceil($half_hour*3600)<time()){
            $this->error('订单已超过可以中途开门的时间');
        }else if($data['temp_unlock'] && $order['fee_type']==3 && $half_hour>0 && $order['create_time']+ceil($half_hour*3600)<time()){
            $this->error('订单已超过可以中途开门的时间');
        }

        $orders = [];

        $this->app->db->startTrans();
        try{
            $res = 1;
            if($data['temp_unlock']<1){
                $data['reality_end_time'] = time();
                $data['status'] = 2;
                unset($data['order_id'], $data['temp_unlock']);

                if($order['fee_type']==3){
                    if($order['refund']>0){
                        $extends['refund'] = $order['refund'];
                        $refund = OrderService::instance()->refund($order);
                        if(is_array($refund)){
                            $data['out_refund_no'] = $refund['out_refund_no'];
                            if(empty($refund['out_refund_no'])){
                                $extends['refund_error'] = $refund['err_code_des'];
                            }
                            $data['extends'] = json_encode($extends,256);
                        }
                        $data['amount_total'] = $order['money']-$order['refund']+$out_money;
                    }else{
                        $data['amount_total'] = $order['money']+$out_money;
                    }
                }else{
                    $data['amount_total'] = $order['money'];
                }

                $res_a = DevicesService::instance()->usePickUpCode(['order_id'=>$order['id'], 'user_id'=>$this->uuid]);
                $res1 = OrderService::instance()->endOrder($order['id'], $data);
                $res2 = BoxService::instance()->updateBox($order['box_id'], ['box_status'=>1, 'user_id'=>0]);

                if($res_a && $res1 && $res2){
                    $order['reality_end_time'] = $data['reality_end_time'];
                    $order['status'] = $data['status'];

                    if($this->user['openid2']){
                        OrderService::instance()->sendTemp(2,$this->user['openid2'], [$device['device_name'], ($box['box_num']<10?'0'.$box['box_num']:$box['box_num']).'号', sprintf("%.2f", $data['amount_total']).'元', date("Y-m-d H:i", $order['create_time']), date("Y-m-d H:i")],'','感谢你的使用！如有物品未取，请联系：'.($extends['store_contact']??''));
                    }

                    $this->app->db->commit();
                }else{
                    $this->app->db->rollback();
                    $this->error("失败");
                }
            }

            //添加redis 开锁命令
            $redis->handler()->ZADD("UnLockCommand", $order['user_id'], $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);

            $user = $this->user;

            $conn = $temp_unlock?'临时开锁':"结束订单";

            jcgoplog('开锁','用户'.$conn.'开锁', $user['nickname'], $user['id'], $order['id'], $order['box_id']);

            if($this->user['openid2']){
                //添加redis 未关门提醒
                $noLockMsg = [time(), 4, $this->user['openid2'], [$extends['device_name'], ($extends['box_num']<10?'0'.$extends['box_num']:$extends['box_num']).'号', date('Y-m-d H:i:s')], '请及时关好柜门，感谢您的配合。如有问题，请联系：'.($extends['store_contact']??'')];
                $redis->set($order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel'], $noLockMsg, 22*60);
            }

            if($res){
                $device = DevicesService::instance()->getDevice($order['d_id']);
                $msg = ['type'=>2, 'device_id'=>$device['device_id'], 'ch'=>$order['board_channel'], 'device_board'=>$order['device_board']];
                SocketService::instance()->sendSocket($msg);

                //增加临时开门命令
                $redis->set($msg['device_id'].$msg['device_board'].$msg['ch'], $order['id'],7200);

                $orders[] = $order;

                OrderService::instance()->buildItemData($orders, 2, $this->user['phone']??'');

                //删除超时定时任务对应订单缓存
                OrderService::instance()->clearOutTimeOrderCache($order);

                //移除已使用的箱子ID
                $redis->handler()->ZREM("orderUseBox".$order['d_id'], $order['box_id']);
            }else{
                $this->error("失败");
            }
        }catch (\Exception $e){
            $this->app->db->rollback();
            $this->error("失败：".$e->getMessage().$e->getTraceAsString());
        }

        if($temp_unlock){
            $redis->set("TempLock".$order['id'], $order['id'], 30);
        }

        $this->success('成功', $orders[0]);
    }

    /**
     * 未开锁（2小时内再次开锁）
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function re_unlock(){
        $data = $this->_vali([
            'order_id.require'     => '订单ID不能为空',
        ]);

        $map = ['id' => $data['order_id'], 'user_id'=>$this->uuid];
        $order = OrderService::instance()->get('', $map);
        if (empty($order)) $this->error('读取订单失败');

        $device = DevicesService::instance()->getDevice($order['d_id']);
        if(empty($device)){
            $this->error("设备不存在");
        }

        $redis = new Redis();
        $cmd = $redis->handler()->ZRANK("UnLockCommand", $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);
        if(!is_numeric($cmd)){
            $this->error("箱柜已经开启过了");
        }

        switch ($order['status']){
            case 0:
                $this->error('订单尚未支付');
                break;
        }

        $box = BoxService::instance()->getBox($order['box_id']);
        if($box['is_lock']!=1 || $box['box_status']==3){
            $msg = ['type'=>1, 'device_id'=>$device['device_id'], 'device_board'=>$order['device_board']];
            SocketService::instance()->sendSocket($msg, 1);
            usleep(600000);
            $box = BoxService::instance()->getBox($order['box_id']);
        }

        if(!($box['box_num']??0)){
            $this->error("箱格异常");
        }if($box['box_status']>2){
            $this->error("箱格处于".BoxService::instance()->box_status[$box['box_status']].'状态');
        }else if($box['user_id'] && $box['user_id']!=$this->uuid){
            $this->error("箱格已被他人使用");
        }else if($box['is_lock']<1){
            $this->error("箱门已打开，请先关闭箱门");
        }else if($box['is_lock']==2){
            $this->error("箱格锁异常");
        }

        $boxs[] = $box??[];

        BoxService::instance()->buildData($boxs, $device['fee_id']);

        $msg = ['type'=>2, 'device_id'=>$device['device_id'], 'ch'=>$order['board_channel'], 'device_board'=>$order['device_board']];

        //添加redis 开锁命令
        $redis->handler()->ZADD("UnLockCommand", $order['user_id'], $order['d_id'].'_'.$order['device_board'].'_'.$order['board_channel']);

        $user = $this->user;
        jcgoplog('开锁','再次开锁', $user['nickname'], $user['id'], $order['id'], $order['box_id']);

        //再次开锁，直接推送命令
        SocketService::instance()->sendSocket($msg, 5);

        $this->success("成功");
    }
}